package eduware.util;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.StringReader;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;

import net.fortuna.ical4j.data.CalendarBuilder;
import net.fortuna.ical4j.filter.Filter;
import net.fortuna.ical4j.filter.PeriodRule;
import net.fortuna.ical4j.model.Calendar;
import net.fortuna.ical4j.model.Component;
import net.fortuna.ical4j.model.DateTime;
import net.fortuna.ical4j.model.Dur;
import net.fortuna.ical4j.model.Period;

/**
 * This class loads an ical calendar from an Internet resource, parses it and
 * stores it in a local backup for the case that there is no Internet
 * connection.
 * 
 * @author Florian Bausch
 * 
 */
public class ICalWrapper implements WritesConfig {

	Calendar calendar = null;
	String url;

	/**
	 * This method enables you to load a ical calendar from an URL (
	 * <code>url</code>) or from a local backup resource (
	 * <code>localCalendar</code>). It parses the calendar file and stores a
	 * local backup to <code>localCalendar</code>).
	 * 
	 * @param url
	 *            An URL as string to a calendar (exact path, with "http://")
	 */
	public ICalWrapper(String url) {
		try {
			Config.getInstance().registerImplementedInterface(this);
			this.url = url;
			StringBuffer newText = new StringBuffer();
			String current = "";
			String last = "";
			String localCalendar = "cal/basic.ics";
			(new File("cal/")).mkdirs();

			Vector<String> newLines = new Vector<String>();

			CalendarBuilder builder = new CalendarBuilder();
			StringReader sin;
			BufferedReader buff;

			// Load calendar
			if ((buff = FileHandler.getReader(url, "UTF8", 1000)) == null)
				if ((buff = FileHandler.getEncryptedReader(localCalendar,
						"UTF8", 500)) == null)
					return;

			// Remove line breaks and store new generated lines an a Vector
			while ((current = buff.readLine()) != null) {
				if (current.startsWith(" ") || last.equals("")) {
					last += current.trim();
				} else {
					newLines.add(this.removeColon(last));
					last = current;
				}
			}
			newLines.add(this.removeColon(last));

			buff.close();

			// create a local backup of the calendar file
			BufferedWriter fout = FileHandler.getEncryptedWriter(localCalendar);

			// Connect all lines of the calendar to one String and write them to
			// a local backup
			for (String line : newLines) {
				newText.append(line);
				newText.append("\n");
				fout.write(line);
				fout.newLine();
			}

			fout.close();

			// Parse String into calendar
			sin = new StringReader(newText.toString());
			this.calendar = builder.build(sin);
		} catch (Exception e) {
			e.printStackTrace();
			this.calendar = null;
			return;
		}
	}

	/**
	 * Use this method to get the calendar.
	 * 
	 * @return The <code>Calendar</code> or <code>null</code>, if the calendar
	 *         could not be loaded.
	 */
	public Calendar getCalendar() {
		return this.calendar;
	}

	/**
	 * Looks for current events.
	 * 
	 * @return A Vector&lt;String&gt; containing the names.
	 */
	public Vector<String> getCurrentEvents() {
		if (this.calendar == null)
			return new Vector<String>();
		java.util.Calendar today = java.util.Calendar.getInstance();

		// create a period starting now with a duration of one 1 second.
		Period period = new Period(new DateTime(today.getTime()), new Dur(0, 0,
				0, 1));
		PeriodRule p = new PeriodRule(period);
		PeriodRule[] ps = new PeriodRule[1];
		ps[0] = p;
		Filter f = new Filter(ps, 0);

		@SuppressWarnings("unchecked")
		Collection<Component> events = f.filter(calendar
				.getComponents(Component.VEVENT));
		Iterator<Component> iter = events.iterator();
		Vector<String> results = new Vector<String>();
		while (iter.hasNext()) {
			Component c = iter.next();
			results.add(c.getProperty("Summary").getValue());
		}
		return results;
	}

	/**
	 * Looks for events that will start in the given interval, but are not
	 * running at the moment.
	 * 
	 * @return A Vector&lt;String&gt; containing the names.
	 */
	public Vector<String> getEventsStartingInInterval(int day, int hour,
			int min, int sec) {
		if (this.calendar == null)
			return new Vector<String>();
		java.util.Calendar today = java.util.Calendar.getInstance();

		// create a period starting now with a given duration
		Period period = new Period(new DateTime(today.getTime()), new Dur(day,
				hour, min, sec));
		PeriodRule p = new PeriodRule(period);
		PeriodRule[] ps = new PeriodRule[1];
		ps[0] = p;
		Filter f = new Filter(ps, 0);
		Vector<String> currentEvents = getCurrentEvents();

		@SuppressWarnings("unchecked")
		Collection<Component> events = f.filter(calendar
				.getComponents(Component.VEVENT));
		Iterator<Component> iter = events.iterator();
		Vector<String> results = new Vector<String>();
		while (iter.hasNext()) {
			Component c = iter.next();
			if (!currentEvents.contains(c.getProperty("Summary").getValue()))
				results.add(c.getProperty("Summary").getValue());
		}
		return results;
	}

	/**
	 * Looks for all events of this day.
	 * 
	 * @return A Vector&lt;String&gt; containing the names.
	 */
	public Vector<String> getTodaysEvents() {
		if (this.calendar == null)
			return new Vector<String>();
		java.util.Calendar today = java.util.Calendar.getInstance();
		today.set(java.util.Calendar.HOUR_OF_DAY, 0);
		today.clear(java.util.Calendar.MINUTE);
		today.clear(java.util.Calendar.SECOND);

		// create a period starting now with a duration of one 1 day.
		Period period = new Period(new DateTime(today.getTime()), new Dur(1, 0,
				0, 0));
		PeriodRule p = new PeriodRule(period);
		PeriodRule[] ps = new PeriodRule[1];
		ps[0] = p;
		Filter f = new Filter(ps, 0);

		@SuppressWarnings("unchecked")
		Collection<Component> eventsToday = f.filter(calendar
				.getComponents(Component.VEVENT));
		Iterator<Component> iter = eventsToday.iterator();
		Vector<String> results = new Vector<String>();
		while (iter.hasNext()) {
			Component c = iter.next();
			results.add(c.getProperty("Summary").getValue());
		}
		return results;
	}

	/**
	 * Writes values to a HashMap that stores configuration values.
	 * 
	 * @param config
	 *            The HashMap&lt;String, String&gt; where the configuration
	 *            shall be written to.
	 */
	public void writeConfig(HashMap<String, String> config) {
		config.put("calendarURL", this.url);
	}

	/**
	 * Removes the ":" in strings with "CN=".
	 * 
	 * @param s
	 *            A String.
	 * @return A String that can be parsed by ical4j.
	 */
	private String removeColon(String s) {
		if (!s.contains("CN="))
			return s;
		String[] parts = s.split(";");
		StringBuffer sb = new StringBuffer();
		int i;
		for (i = 0; i < parts.length; i++) {
			if (parts[i].startsWith("CN="))
				parts[i] = parts[i].replace(":", "");
			sb.append(parts[i]);
			sb.append(";");
		}
		sb.deleteCharAt(sb.length() - 1);
		return sb.toString();
	}
}
